package com.kun.video.ui.window;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.kun.video.AbstractApplication;
import com.kun.video.context.ContextService;
import com.kun.video.domain.KunFileWrapper;
import com.kun.video.domain.StageSize;
import com.kun.video.domain.VideoDataStaticContext;
import com.kun.video.thread.ComThreadPool;
import com.kun.video.ui.ComponentUtil;
import com.kun.video.ui.KunComponent;
import com.kun.video.ui.control.KunTextArea;
import com.kun.video.ui.control.button.AlertButton;
import com.kun.video.ui.control.button.KunButton;
import com.kun.video.ui.dialog.ConfirmDialog;
import com.kun.video.ui.pane.KunHorBox;
import com.kun.video.ui.pane.KunImageViewBox;
import com.kun.video.ui.pane.KunProgressBox;
import com.kun.video.ui.pane.RemoveFixedWatermarkBox;
import com.kun.video.util.ComUtil;
import com.kun.video.util.CommandExecutor;
import com.kun.video.util.CssManager;
import javafx.beans.value.ChangeListener;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;


/**
 * TODO
 *
 * @author gzc
 * @since 2025/3/9
 **/
public class VideoButtonView extends StackPane implements KunComponent {
    private static final Logger log = LoggerFactory.getLogger(VideoButtonView.class);
    private static final String CSS_CLASS = "video-button-view";
    /**
     * 主容器
     */
    private final VBox mainBox = new VBox();
    /**
     * 输出容器
     */
    private final KunTextArea textArea = new KunTextArea();
    /**
     * 进度条容器
     */
    private final KunHorBox progressHorBox = new KunHorBox();
    private final KunProgressBox progressBox = new KunProgressBox();
    /**
     * 输入容器
     */
    private final KunHorBox selectVideoBox = new KunHorBox();
    private final KunButton selectVideoButton = new KunButton("选择视频", "选择视频");
    private final Label selectVideoLabel = new Label();
    private final AlertButton settingButton = new AlertButton("设置", null, "打开设置窗口", null);
    /**
     * 视频信息容器
     */
    private final KunHorBox videoInfoBox = new KunHorBox();
    private final KunButton viewVideoInfoButton = new KunButton("视频信息", "输出视频详细信息");
    /**
     * 视频转换按钮容器
     */
    private final KunHorBox videoConvertBox = new KunHorBox();
    private final KunButton addMoveWatermarkButton = new KunButton("移动文本水印", "在视频中添加随时间而有规律移动的文本水印");
    private final KunButton addFixedWatermarkButton = new KunButton("固定文本水印", "在视频中添加固定位置的文本水印");
    private final KunButton removeFixedWatermarkButton = new KunButton("去除水印", "去除水印");
    private final AlertButton removeFixedWatermarkEnhancedButton = new AlertButton("去水印2.0");
    /**
     * 视频压缩按钮容器
     */
    private final KunHorBox compressBox = new KunHorBox();
    private final KunButton compressButton = new KunButton("压缩", "视频压缩");
    private final KunButton removeDuplicateButton = new KunButton("去重", "视频去重");

    /**
     * 工具
     */
    private ComThreadPool comThreadPool;
    private KunImageViewBox watermarkSelector;

    public VideoButtonView() {
        initialize();
    }

    @Override
    public Parent getNode() {
        return this;
    }

    @Override
    public String getStyleName() {
        return CSS_CLASS;
    }

    @Override
    public void initLayout() {
        progressHorBox.getChildren().addAll(progressBox);

        selectVideoBox.getChildren().addAll(VideoDataStaticContext.SELECT_VIDEO_BUTTON, selectVideoLabel, viewVideoInfoButton);
        videoInfoBox.getChildren().addAll(settingButton);
        videoConvertBox.getChildren().addAll(removeFixedWatermarkEnhancedButton);
        compressBox.getChildren().addAll(compressButton, removeDuplicateButton);
        mainBox.getChildren().addAll(textArea, progressHorBox, selectVideoBox, videoInfoBox, videoConvertBox, compressBox);
        getChildren().addAll(mainBox);
    }

    @Override
    public void initStyle() {
        StackPane.setAlignment(mainBox, Pos.TOP_CENTER);
        textArea.setEditable(false);
        // 移除焦点边框样式
        CssManager.getInstance().disableFocusedBorderStyle(textArea);
        selectVideoLabel.setAlignment(Pos.CENTER_LEFT);
    }

    @Override
    public void initDimensions() {
    }

    @Override
    public void reactiveDimensions(double width, double height) {
        mainBox.setPrefSize(width, height);
        mainBox.setMaxSize(width, height);
        // 输出容器
        textArea.setPrefSize(width, height * 0.5);
        textArea.setMaxSize(width, height * 0.5);
        // 进度条
        progressBox.setPrefSize(width, selectVideoBox.getPrefHeight());
        progressBox.setMaxSize(width, selectVideoBox.getPrefHeight());
        // 输入视频容器
        selectVideoLabel.setPrefSize(width - 220, selectVideoBox.getPrefHeight());
        selectVideoLabel.setMaxSize(width - 220, selectVideoBox.getPrefHeight());

    }

    private SettingView settingView;

    @Override
    public void initData() {
        progressBox.getProgressBar().setProgress(0.01D);
        comThreadPool = ContextService.getBean(ComThreadPool.class);
        settingButton.layoutHandler(dialog -> {
            if (settingView == null) {
                settingView = new SettingView(new StageSize(800, 600));
            }
            dialog.setCenter(settingView);
        });
        // 处理配置窗口结果
        settingButton.resultHandler(result -> {
            try {
                if (result) {
                    settingView.save();
                }
            } catch (Exception e) {
                log.error("发送异常 ", e);
            }
            if (settingView != null) {
                settingView.dispose();
                settingView = null;
            }
        });
        AtomicReference<RemoveFixedWatermarkBox> removeFixedWatermarkBoxAtomicReference = new AtomicReference<>();
        removeFixedWatermarkEnhancedButton.showLayoutHandler(dialog -> {
            RemoveFixedWatermarkBox removeFixedWatermarkBox;
            KunFileWrapper kunFileWrapper = VideoDataStaticContext.SELECT_FILE.get();
            if (kunFileWrapper != null) {
                removeFixedWatermarkBox = new RemoveFixedWatermarkBox(kunFileWrapper.getInputFile());
            } else {
                removeFixedWatermarkBox = new RemoveFixedWatermarkBox(null);
            }
            removeFixedWatermarkBoxAtomicReference.set(removeFixedWatermarkBox);
            dialog.setCenter(removeFixedWatermarkBox);
        });
        removeFixedWatermarkEnhancedButton.resultHandler(result -> {
            if (result) {
                comThreadPool.runAsync(() -> {
                    try {
                        removeFixedWatermarkBoxAtomicReference.get().resultHandler(this::updateTextArea, this::updateProgress);
                    } catch (Exception e) {
                        log.error("发送异常 ", e);
                    }
                });
            }
        });

        // 绑定
        VideoDataStaticContext.SELECT_FILE.addListener((observable, oldValue, newValue) -> {
//            selectVideoLabel.setText(nv != null ? StrUtil.join("，", nv.stream().map(KunFileWrapper::getInputFileNameAndFormat).toList()) : "");
            if (newValue != null) {
                selectVideoLabel.setText(newValue.getInputFileNameAndFormat());
                ComponentUtil.setTooltip(selectVideoLabel, newValue.getAbsolutePath());
            }
        });
    }

    public KunTextArea getTextArea() {
        return this.textArea;
    }

    private final ChangeListener<List<KunFileWrapper>> updateSelectVideoLabelChangeListener = (obs, ov, nv) -> ComUtil.uiThreadRun(() -> {
        selectVideoLabel.setText(nv != null ? StrUtil.join("，", nv.stream().map(KunFileWrapper::getInputFileNameAndFormat).toList()) : "");
        ComponentUtil.setTooltip(selectVideoLabel, selectVideoLabel.getText());
    });
    /**
     * 查看视频信息
     */
    private final EventHandler<? super MouseEvent> viewVideoInfoEventHandler = event -> {
        KunFileWrapper kunFileWrapper = VideoDataStaticContext.SELECT_FILE.get();
        if (kunFileWrapper == null) {
            return;
        }
        CommandExecutor.CommandResult result = CommandExecutor.executeCommand("ffmpeg", "-i", kunFileWrapper.getAbsolutePath());
        ComUtil.uiThreadRun(() -> textArea.appendText(result.getOutputStr()));
    };
    /**
     * 添加移动文本水印
     */
    private final EventHandler<? super MouseEvent> addMoveWatermarkEventHandler = event -> {
        KunFileWrapper kunFileWrapper = VideoDataStaticContext.SELECT_FILE.get();
        if (kunFileWrapper == null) {
            return;
        }
        File outputFile = kunFileWrapper.getOutputFile("移动文字水印");
        if (!isOverwriteOutputFileBlockThread(outputFile)) {
            return;
        }
        String drawTextCommandStr = "\"drawtext=text='笋嘢666':fontfile='C\\:/Windows/Fonts/simhei.ttf':fontsize=40:fontcolor=white@0.1:x='if(gt(mod(90*t,2*(w-tw)),w-tw),2*(w-tw)-mod(90*t,2*(w-tw)),mod(90*t,2*(w-tw)))':y='if(gt(mod(60*t,2*(h-th)),h-th),2*(h-th)-mod(60*t,2*(h-th)),mod(60*t,2*(h-th)))',scale=iw:-2:flags=lanczos\"";
        String[] commandStr = {"ffmpeg", "-i", kunFileWrapper.getAbsolutePath(), "-vf", drawTextCommandStr, "-c:v", "libx264", "-crf", "10", "-preset", "slower", "-pix_fmt", "yuv420p", "-profile:v", "high", "-x264-params", "\"keyint=240:min-keyint=24:no-fast-pskip=1\"", "-c:a", "copy", "-movflags", "+faststart", "-y", outputFile.getAbsolutePath()};
        CommandExecutor.executeCommand(Arrays.asList(commandStr), (line) -> {
            updateTextArea(line);
            updateProgress("添加移动文字水印", line);
        });
    };
    /**
     * 压缩视频
     */
    private final Runnable compressEventHandler = () -> {
        log.info("执行压缩视频");
        KunFileWrapper kunFileWrapper = VideoDataStaticContext.SELECT_FILE.get();
        if (kunFileWrapper == null) {
            return;
        }
        File outputFile = kunFileWrapper.getOutputFileByName("f:\\", kunFileWrapper.getInputFileNameAndFormat());
        if (!isOverwriteOutputFileBlockThread(outputFile)) {
            return;
        }
        String[] commandStr = {"ffmpeg", "-i", kunFileWrapper.getAbsolutePath(), "-threads", "4", "-thread_queue_size", "512", "-s", "1280x720", "-r", "30", "-b:v", "2500k", "-c:v", "libx264", "-preset", "fast", "-b:a", "64k", "-ar", "24000", "-ac", "2", "-y", outputFile.getAbsolutePath()};
        CommandExecutor.executeCommand(Arrays.asList(commandStr), (line) -> {
            updateTextArea(line);
            updateProgress("压缩视频【" + kunFileWrapper.getInputFileNameAndFormat() + "】", line);
        });
    };

    @Override
    public void initListeners() {
        ComponentUtil.weakActionEventHandler(viewVideoInfoButton, viewVideoInfoEventHandler);
        ComponentUtil.weakActionEventHandler(addMoveWatermarkButton, addMoveWatermarkEventHandler);
        ComponentUtil.weakActionEventHandler(compressButton, compressEventHandler);
    }

    @Override
    public void dispose() {
        ComponentUtil.removeWeakListener(viewVideoInfoButton, viewVideoInfoEventHandler);
        ComponentUtil.removeWeakListener(addMoveWatermarkButton, addMoveWatermarkEventHandler);
        ComponentUtil.removeWeakListener(compressButton, compressEventHandler);

    }

    /**
     * 弹出确认窗口是否重写输出文件（阻塞线程，选择确认框按钮后继续执行原线程代码逻辑）
     *
     * @param outputFile 输出文件对象
     * @return 是否重写输出文件
     */
    private boolean isOverwriteOutputFileBlockThread(File outputFile) {
        if (!outputFile.exists()) {
            return true;
        }
        AtomicBoolean result = new AtomicBoolean(true);
        // 使用 CountDownLatch 实现线程同步
        CountDownLatch latch = new CountDownLatch(1);
        ComUtil.uiThreadRun(() -> {
            try {
                log.info("是否覆盖文件{}", outputFile.getAbsolutePath());
                // 弹出确认窗口
                ConfirmDialog confirmDialog = new ConfirmDialog("覆盖文件确认", AbstractApplication.getAppStage());
                confirmDialog.setCenter(new Label("是否覆盖文件" + outputFile.getAbsolutePath()));
                if (!confirmDialog.showAndWaitResult()) {
                    result.set(false);
                }
            } finally {
                // 释放锁
                latch.countDown();
            }
        });
        try {
            // 阻塞当前线程，等待用户交互完成
            latch.await();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.warn("线程被中断：{}", e.getMessage());
        }
        return result.get();
    }

    private void updateTextArea(String line) {
        ComUtil.uiThreadRun(() -> {
            textArea.appendText("\n" + line);
            // 滚动到最底部
            textArea.setScrollTop(Double.MAX_VALUE);
        });
    }

    private final AtomicLong duration = new AtomicLong(0);

    private void updateProgress(String taskName, String lineStr) {
        if (lineStr == null) {
            return;
        }
        if (lineStr.contains("Duration") && duration.get() == 0) {
            String subBetween = StrUtil.subBetween(lineStr, "Duration: ", ",");
            int millisecond = DateUtil.timeToSecond(DateUtil.parseTime(subBetween).toTimeStr());
            duration.set(millisecond);
        }
        int nTimeStartIndex = lineStr.indexOf("time=");
        int nTimeEndIndex = lineStr.indexOf(" bitrate=");
        if (nTimeStartIndex > 0 && nTimeEndIndex > 0) {
            String timeStr = lineStr.substring(nTimeStartIndex + 6, nTimeEndIndex);
            if (StrUtil.isNotBlank(timeStr) && duration.get() != 0) {
                int currMillisecond = DateUtil.timeToSecond(DateUtil.parseTime(timeStr.trim()).toTimeStr());
                double doubleValue = new BigDecimal(currMillisecond).divide(new BigDecimal(duration.get()), 4, RoundingMode.HALF_DOWN).doubleValue();
                // 更新进度条
                ComUtil.uiThreadRun(() -> {
                    progressBox.setProgress(doubleValue);
                    progressBox.setTaskLabel(taskName + progressBox.getProgressBar().getProgressLabel().getText());
                });
            }
        }
    }

    public void setWatermarkSelector(KunImageViewBox watermarkSelector) {
        this.watermarkSelector = watermarkSelector;
    }
}
